package finalfinancialanalyst.business;

import java.math.BigDecimal;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Map;
import java.util.Date;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.util.SortOrder;

public class Account implements Serializable {
    
        private static final long serialVersionUID = 1L;
 
	private String name;
	 
	private BigDecimal startBalance;
	 
	private String type;
	 
	private ArrayList<Budget> registeredBudgets;
	 
	private Currency currency;
	 
	private ArrayList<Transaction> transactions;
	 
	public Account(String name, Currency currency, BigDecimal startBalance, String type) {
            transactions = new ArrayList<Transaction>();
            registeredBudgets = new ArrayList<Budget>();
            
            this.name = name;
            this.currency = currency;
            this.startBalance = startBalance;
            this.type = type;
            
	}
	 
	public Account() {
            this("",CurrencyList.getInstance().retrieveCurrencyBySymbol("EUR"),BigDecimal.ZERO,"");
	}
        
        public void setCurrency(Currency cur) {
		currency = cur;
	}
	 
	public Money getBalance() {
             return getBalance(null);
	}
        
        public Money getBalance(Date toDate) {
            BigDecimal balance = startBalance;
            for (Transaction tran : transactions) {
                if (toDate == null || tran.getTime().before(toDate)) {
                    if (tran.getIsExpenditure()) {
                        balance = balance.subtract(tran.getAccountAmount().getAmount());
                    }
                    else {
                        balance = balance.add(tran.getAccountAmount().getAmount()); 
                    }
                }
            }
            return new Money(balance.toString(), currency);  
        }
        
        public int transactionCount() {
            return transactions.size();
        }
        
        @Override
        public String toString() {
           return this.name; 
        }
        
        
        public Transaction getTransaction(int rowindex) {
            return transactions.get(rowindex);
        }
	 
	public boolean addTransaction(Transaction tran) {
		return (transactions.add(tran));
	}
        
        public boolean removeTransaction(Transaction tran) {
            return transactions.remove(tran);
        }
	 
	public ArrayList getTransactionsList() {
		return transactions;
	}
	 
	public Currency getCurrency() {
		return currency;
	}
	 
	public void setName(String name) {
                this.name = name;
	}
	 
	public String getName() {
		return name;
	}
	 
	public void setStartBalance(BigDecimal balance) {
                this.startBalance = balance;
	}
	 
	public BigDecimal getStartBalance() {
		return startBalance;
	}
	 
	public void setType(String type) {
                this.type = type;
	}
	 
	public String getType() {
		return type;
	}
	 
	/**
	 * Register Budget to alarm list.
	 * 
	 * Every time when there is a transaction stored to this account, the observing Budgets will be alarmed
	 * 
	 * Part of Observer Design Pattern
	 */
	public boolean registerBudget(Budget bud) {
		return (registeredBudgets.add(bud));
	}
	 
	/**
	 * Unregister budget from alarm list
	 * 
	 * Part of Observer Design Pattern
	 */
	public boolean unregisterBudget(Budget bud) {
		return (registeredBudgets.remove(bud));
	}
	 
	public void alarmBudgetSubscribers() {
	   for (Budget bud : registeredBudgets) {
               bud.accountChangedNotification(this);
           }
	}
	 
	/**
	 * Returns statistics about minimum spendings for the transactions contained. All parameters are optional and ignored if not specified
	 * 
	 * Data are returned in format "Description - number" so they can be shown both in a table or in a graph
	 */
	public Map getMinimum(String category, Date dateStart, Date dateEnd) {
		return null;
	}
	 
	/**
	 * Returns statistics about maximum spendings for the transactions contained. All parameters are optional and ignored if not specified
	 * 
	 * Data are returned in format "Description - number" so they can be shown both in a table or in a graph
	 */
	public Map getMaximum(String category, Date dateStart, Date dateEnd) {
		return null;
	}
	 
	/**
	 * Returns statistics about median spendings for the transactions contained. All parameters are optional and ignored if not specified
	 * 
	 * Data are returned in format "Description - number" so they can be shown both in a table or in a graph
	 */
	public Map getMedian(String category, Date dateStart, Date dateEnd) {
		return null;
	}
	 
	/**
	 * Returns statistics about average spendings for the transactions contained. All parameters are optional and ignored if not specified
	 * 
	 * Data are returned in format "Description - number" so they can be shown both in a table or in a graph
	 */
	public Map getAverage(String category, Date dateStart, Date dateEnd) {
		return null;
	}
        
        public PieDataset getCategoryExpenditureStats(Date dateStart, Date dateEnd, Currency curr) {
           DefaultPieDataset dataset = new DefaultPieDataset();
            
           for (Transaction trans : transactions) {
                if (trans.getIsExpenditure()  && trans.getTime().before(dateEnd) && trans.getTime().after(dateStart)) {
                    String cat = trans.getCategory();
                    int catkey = dataset.getIndex(cat);
                    BigDecimal amount = trans.getAccountAmount().convertTo(curr).getAmount();

                    if (catkey != -1) {
                        amount = amount.add(BigDecimal.valueOf(dataset.getValue(catkey).doubleValue()));
                    }
                    dataset.setValue(cat, amount.doubleValue());
                }
                
            } 
            dataset.sortByValues(SortOrder.DESCENDING);
            return dataset;
        }
        
        public TimeSeriesCollection getBalanceStats(Date dateStart, Date dateEnd, Currency curr) {
            TimeSeriesCollection dataset = new TimeSeriesCollection();
            TimeSeries series = new TimeSeries(getName(), Day.class);
            Date curDate = dateStart;
            while(curDate.before(dateEnd)) {
            
                series.add(new Day(curDate), getBalance(curDate).convertTo(curr).getAmount());
                
                //increment
                Calendar cal = Calendar.getInstance();
                cal.setTime(curDate);
                cal.add(Calendar.DAY_OF_MONTH, 1);
                curDate = cal.getTime();
            }
        
            dataset.addSeries(series);
            return dataset;
        }

}
 
